import {
    LoggingDebugSession,
    InitializedEvent, TerminatedEvent, StoppedEvent, ContinuedEvent, OutputEvent,
    Thread, StackFrame, Scope, Source, Handles, Breakpoint, BreakpointEvent
} from '@vscode/debugadapter';
import { DebugProtocol } from '@vscode/debugprotocol';
import { spawn, ChildProcess } from 'child_process';
import * as path from 'path';
import * as os from 'os';
import { BreakpointManager } from './breakpointManager';
import { Logger } from './logger';
import { IDebuggerConfig } from './debugConfig';
import { IBreakpoint } from './types';
import * as fs from 'fs';

interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
    program: string;
    args?: string[];
    cwd?: string;
    env?: { [key: string]: string };
    stopAtEntry?: boolean;
    console?: string;
    justMyCode?: boolean;
}

export class DotNetDebugSession extends LoggingDebugSession {
    private static THREAD_ID = 1;
    private _runtime: ChildProcess | undefined;
    private _variableHandles = new Handles<string>();
    private _breakpointManager: BreakpointManager;
    private logger: Logger;

    public constructor() {
        super("dotnet-debug.txt");
        this.setDebuggerLinesStartAt1(true);
        this.setDebuggerColumnsStartAt1(true);

        // 初始化 Logger
        Logger.initialize();
        this.logger = Logger.getInstance();
        this.logger.debug('DotNetDebugSession 初始化');

        const debuggerConfig: IDebuggerConfig = {
            debuggerPath: 'dotnet',
            debuggerPort: 4711,
            debuggerHost: 'localhost',
            timeout: 5000,
            enableSourceMap: true,
            enableHotReload: false,
            enableConditionalBreakpoints: true,
            enableDataBreakpoints: true,
            enableExceptionBreakpoints: true,
            logLevel: 'debug' as const,
            maxVariableLength: 1000,
            maxStringLength: 1000,
            maxArrayLength: 100,
            evaluationTimeout: 5000
        };

        this._breakpointManager = new BreakpointManager(debuggerConfig);
        this.logger.debug('断点管理器初始化完成');
    }

    protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {
        response.body = response.body || {};
        response.body.supportsConfigurationDoneRequest = true;
        response.body.supportsEvaluateForHovers = true;
        response.body.supportsStepBack = false;
        response.body.supportsSetVariable = true;
        response.body.supportsRestartFrame = true;
        response.body.supportsStepInTargetsRequest = true;
        response.body.supportsDelayedStackTraceLoading = true;
        response.body.supportsLoadedSourcesRequest = true;
        response.body.supportsTerminateRequest = true;
        response.body.supportsBreakpointLocationsRequest = true;
        response.body.supportsConditionalBreakpoints = true;
        response.body.supportsHitConditionalBreakpoints = true;
        response.body.supportsLogPoints = true;

        this.sendResponse(response);
        this.sendEvent(new InitializedEvent());
    }

    protected async launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): Promise<void> {
        try {
            // 获取项目文件路径
            const dllPath = this.resolveProgramPath(args.program);
            this.logger.debug(`DLL路径: ${dllPath}`);

            // 从 DLL 路径解析项目目录
            const projectDir = path.dirname(path.dirname(path.dirname(path.dirname(dllPath))));
            this.logger.debug(`项目目录: ${projectDir}`);

            // 从项目目录名称获取项目名
            const projectName = path.basename(projectDir);
            const projectFile = path.join(projectDir, `${projectName}.csproj`);

            this.logger.info(`启动调试会话: ${projectFile}`);
            this.logger.debug(`工作目录: ${projectDir}`);
            this.logger.debug(`环境变量: ${JSON.stringify(args.env)}`);

            // 启动 dotnet 进程前先验证项目文件存在
            try {
                await fs.promises.access(projectFile);
                this.logger.debug(`项目文件存在: ${projectFile}`);
            } catch (error) {
                throw new Error(`项目文件不存在: ${projectFile}`);
            }

            // 等待断点设置完成
            await new Promise(resolve => setTimeout(resolve, 2000));

            // 启动 dotnet 进程
            this._runtime = spawn('dotnet', [
                'build',  // 先构建项目
                projectFile,
                '--configuration', 'Debug',  // 指定 Debug 配置
                '/property:GenerateFullPaths=true',
                '/consoleloggerparameters:NoSummary',
                '/p:DebugType=portable'
            ], {
                cwd: projectDir,
                env: {
                    ...process.env,
                    ...args.env,
                    DOTNET_CLI_UI_LANGUAGE: 'en-US',
                    DOTNET_ENVIRONMENT: 'Development',
                    VSDBG_BREAKPOINT_VERIFICATION: '1',
                    VSDBG_LOGGING: '1',
                    VSDBG_JUSTMYCODE: args.justMyCode ? '1' : '0',
                    VSDBG_WAIT_ON_LAUNCH: '1',
                    VSDBG_LAUNCH_DEBUGGER: '1',
                    VSDBG_BREAKPOINT_TRACE: '1',
                    VSDBG_CONNECTION_TRACE: '1',
                    VSDBG_WAIT_FOR_DEBUGGER: '1'
                }
            });

            // 添加构建输出处理
            this._runtime.stdout?.on('data', (data: Buffer) => {
                const output = data.toString();
                this.logger.debug(`构建输出: ${output}`);
                this.sendEvent(new OutputEvent(output, 'stdout'));
            });

            this._runtime.stderr?.on('data', (data: Buffer) => {
                const error = data.toString();
                this.logger.error(`构建错误: ${error}`);
                this.sendEvent(new OutputEvent(error, 'stderr'));
            });

            // 等待构建完成后启动调试
            this._runtime.on('exit', async (buildCode: number) => {
                if (buildCode === 0) {
                    this.logger.info('构建成功，准备启动调试...');

                    // 再次等待以确保所有设置就绪
                    await new Promise(resolve => setTimeout(resolve, 2000));

                    // 构建成功，启动调试
                    this._runtime = spawn('dotnet', [
                        'run',
                        '--no-build',  // 跳过构建
                        '--project', projectFile,
                        ...(args.args || [])
                    ], {
                        cwd: projectDir,
                        env: {
                            ...process.env,
                            ...args.env,
                            DOTNET_CLI_UI_LANGUAGE: 'en-US',
                            DOTNET_ENVIRONMENT: 'Development',
                            VSDBG_BREAKPOINT_VERIFICATION: '1',
                            VSDBG_LOGGING: '1',
                            VSDBG_JUSTMYCODE: args.justMyCode ? '1' : '0',
                            VSDBG_WAIT_ON_LAUNCH: '1',
                            VSDBG_LAUNCH_DEBUGGER: '1',
                            VSDBG_BREAKPOINT_TRACE: '1',
                            VSDBG_CONNECTION_TRACE: '1',
                            DOTNET_ATTACH_DEBUGGER: '1',
                            COMPlus_ZapDisable: '1',
                            DOTNET_ENABLE_DEBUGGER: '1',
                            VSDBG_WAIT_FOR_DEBUGGER: '1',
                            VSDBG_BREAK_ON_START: '1'  // 添加在启动时中断的环境变量
                        }
                    });

                    // 设置运行时处理程序
                    this.setupRuntimeHandlers(this._runtime, args);

                    // 发送调试器已准备就绪的事件
                    this.sendEvent(new InitializedEvent());
                } else {
                    // 构建失败
                    this.logger.error('项目构建失败');
                    this.sendEvent(new TerminatedEvent());
                }
            });

            this.sendResponse(response);

            if (args.stopAtEntry) {
                this.logger.info('在入口点停止');
                this.sendEvent(new StoppedEvent('entry', DotNetDebugSession.THREAD_ID));
            }
        } catch (error: any) {
            this.logger.error(`启动失败: ${error.message}`);
            response.success = false;
            response.message = error.toString();
            this.sendResponse(response);
        }
    }

    private setupRuntimeHandlers(runtime: ChildProcess, args: LaunchRequestArguments): void {
        let isStarting = true;
        let hasEntryPoint = false;
        let debuggerAttached = false;
        let isPaused = false;

        runtime.stdout?.on('data', async (data: Buffer) => {
            const output = data.toString();
            this.sendEvent(new OutputEvent(output, 'stdout'));
            this.logger.debug(`程序输出: ${output}`);

            if (output.includes('Debugger attached') || output.includes('Debugger successfully attached')) {
                debuggerAttached = true;
                this.logger.info('调试器已附加');

                // 调试器附加后，确保所有断点都已设置
                await new Promise(resolve => setTimeout(resolve, 1000));

                // 重新验证所有断点
                const allBreakpoints = this._breakpointManager.getAllBreakpoints();
                for (const bp of allBreakpoints) {
                    try {
                        await this.sendDebuggerCommand(`break-insert "${bp.source.path}:${bp.line}"`);
                        this.logger.debug(`设置断点: ${bp.source.path}:${bp.line}`);

                        // 验证断点
                        await this.sendDebuggerCommand(`break-info ${bp.id}`);
                    } catch (error) {
                        this.logger.error(`设置断点失败: ${error}`);
                    }
                }

                // 发送初始化完成事件
                this.sendEvent(new InitializedEvent());

                // 如果需要在入口点停止
                if (args.stopAtEntry) {
                    await this.sendDebuggerCommand('break-insert -f main');
                    this.logger.info('在入口点设置断点');
                }
            }

            if (isStarting && (output.includes('Process launched:') || output.includes('Process started'))) {
                isStarting = false;
                hasEntryPoint = true;
                this.logger.info('程序已启动');

                if (!debuggerAttached) {
                    this.logger.info('等待调试器附加...');
                    await new Promise(resolve => setTimeout(resolve, 2000));

                    // 发送中断命令
                    await this.sendDebuggerCommand('interrupt');
                    isPaused = true;
                }

                // 在程序启动时强制暂停
                await this.sendDebuggerCommand('interrupt');
                this.sendEvent(new StoppedEvent('entry', DotNetDebugSession.THREAD_ID));
            }

            // 检查断点验证
            if (output.includes('Breakpoint verified') || output.includes('Breakpoint set:')) {
                const match = output.match(/Breakpoint (?:verified|set): (.*):(\d+)/);
                if (match) {
                    const [, file, line] = match;
                    const lineNumber = parseInt(line, 10);
                    this.logger.info(`断点已验证: ${file}:${lineNumber}`);

                    // 查找并验证对应的断点
                    const normalizedFile = path.normalize(file);
                    const breakpoints = this._breakpointManager.getBreakpoints(normalizedFile);
                    const breakpoint = breakpoints.find(bp => bp.line === lineNumber);
                    if (breakpoint) {
                        breakpoint.verified = true;
                        this.sendEvent(new BreakpointEvent('changed', {
                            verified: true,
                            id: breakpoint.id,
                            line: breakpoint.line,
                            source: breakpoint.source
                        }));

                        // 确保断点已启用
                        await this.sendDebuggerCommand(`break-enable ${breakpoint.id}`);
                    }
                }
            }

            // 检查是否命中断点
            if (output.includes('Breakpoint hit:') || output.includes('Break at:') || output.includes('stopped,')) {
                const match = output.match(/(?:Breakpoint hit|Break at|stopped,): (.*):(\d+)/);
                if (match) {
                    const [, file, line] = match;
                    const lineNumber = parseInt(line, 10);
                    this.logger.info(`命中断点: ${file}:${lineNumber}`);

                    try {
                        // 立即暂停执行
                        isPaused = true;
                        await this.sendDebuggerCommand('interrupt');

                        // 发送停止事件
                        this.sendEvent(new StoppedEvent('breakpoint', DotNetDebugSession.THREAD_ID));

                        // 获取当前范围内的变量
                        const variables = await this.getFrameVariables(DotNetDebugSession.THREAD_ID, 0);
                        const shouldBreak = await this._breakpointManager.shouldBreak(file, lineNumber, variables);

                        if (!shouldBreak) {
                            // 只有在不需要中断时才继续执行
                            isPaused = false;
                            this.logger.debug('断点条件不满足，继续执行');
                            await this.sendDebuggerCommand('continue');
                            this.sendEvent(new ContinuedEvent(DotNetDebugSession.THREAD_ID));
                        }
                    } catch (error) {
                        this.logger.error(`处理断点时出错: ${error}`);
                        // 发生错误时保持暂停状态
                        isPaused = true;
                        this.sendEvent(new StoppedEvent('breakpoint', DotNetDebugSession.THREAD_ID));
                    }
                }
            }
        });

        runtime.stderr?.on('data', (data: Buffer) => {
            const error = data.toString();
            this.logger.error(`程序错误: ${error}`);
            this.sendEvent(new OutputEvent(error, 'stderr'));
        });

        runtime.on('error', (error: Error) => {
            this.logger.error(`运行时错误: ${error.message}`);
            this.sendEvent(new OutputEvent(`Error: ${error.message}\n`));
        });

        runtime.on('exit', (code: number) => {
            this.logger.info(`程序退出，代码: ${code}`);
            this.sendEvent(new TerminatedEvent());
        });
    }

    private async getFrameVariables(threadId: number, frameId: number): Promise<Map<string, any>> {
        try {
            // TODO: 实现从调试器获取变量
            // 这里应该实现与调试器的通信来获取实际的变量值
            return new Map<string, any>();
        } catch (error) {
            this.logger.error(`获取帧变量失败: ${error}`);
            return new Map<string, any>();
        }
    }

    protected async continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): Promise<void> {
        try {
            await this.sendDebuggerCommand('continue');
            this.sendResponse(response);
            this.sendEvent(new ContinuedEvent(args.threadId));
        } catch (error) {
            this.logger.error(`继续执行失败: ${error}`);
            response.success = false;
            response.message = String(error);
            this.sendResponse(response);
        }
    }

    protected async nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): Promise<void> {
        try {
            await this.sendDebuggerCommand('next');
            this.sendResponse(response);
            this.sendEvent(new StoppedEvent('step', DotNetDebugSession.THREAD_ID));
        } catch (error) {
            this.logger.error(`单步执行失败: ${error}`);
            response.success = false;
            response.message = String(error);
            this.sendResponse(response);
        }
    }

    protected async stepInRequest(response: DebugProtocol.StepInResponse, args: DebugProtocol.StepInArguments): Promise<void> {
        try {
            await this.sendDebuggerCommand('step');
            this.sendResponse(response);
            this.sendEvent(new StoppedEvent('step', DotNetDebugSession.THREAD_ID));
        } catch (error) {
            this.logger.error(`步入失败: ${error}`);
            response.success = false;
            response.message = String(error);
            this.sendResponse(response);
        }
    }

    protected async stepOutRequest(response: DebugProtocol.StepOutResponse, args: DebugProtocol.StepOutArguments): Promise<void> {
        try {
            await this.sendDebuggerCommand('finish');
            this.sendResponse(response);
            this.sendEvent(new StoppedEvent('step', DotNetDebugSession.THREAD_ID));
        } catch (error) {
            this.logger.error(`步出失败: ${error}`);
            response.success = false;
            response.message = String(error);
            this.sendResponse(response);
        }
    }

    protected threadsRequest(response: DebugProtocol.ThreadsResponse): void {
        response.body = {
            threads: [
                new Thread(DotNetDebugSession.THREAD_ID, "Main Thread")
            ]
        };
        this.sendResponse(response);
    }

    protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void {
        try {
            this.logger.info('开始处理堆栈跟踪请求...');

            // 获取所有断点信息
            const allBreakpoints = this._breakpointManager.getAllBreakpoints();
            this.logger.debug(`当前所有断点: ${JSON.stringify(allBreakpoints.map(bp => ({
                path: bp.source.path,
                line: bp.line,
                verified: bp.verified
            })))}`);

            // 打印运行时信息
            this.logger.debug(`运行时信息: ${JSON.stringify({
                runtime: this._runtime ? '已启动' : '未启动',
                spawnargs: (this._runtime as any)?.spawnargs || '无参数'
            })}`);

            // 从启动参数中获取程序路径
            const programPath = (this._runtime as any)?.spawnargs?.[2] || '';
            this.logger.debug(`程序路径: ${programPath}`);

            const projectDir = path.dirname(path.dirname(path.dirname(path.dirname(programPath))));
            this.logger.debug(`项目目录: ${projectDir}`);

            // 获取第一个已验证的断点作为源文件
            const verifiedBreakpoint = allBreakpoints.find(bp => bp.verified);
            let sourcePath = programPath;
            let sourceName = path.basename(programPath);

            if (verifiedBreakpoint) {
                sourcePath = verifiedBreakpoint.source.path;
                sourceName = path.basename(sourcePath);
                this.logger.debug(`使用已验证断点的源文件: ${sourcePath}`);
            }

            // 创建堆栈帧
            const stackFrames = [];

            // 如果有已验证的断点，添加对应的堆栈帧
            if (verifiedBreakpoint) {
                stackFrames.push(new StackFrame(
                    0,
                    path.basename(verifiedBreakpoint.source.path),
                    new Source(
                        path.basename(verifiedBreakpoint.source.path),
                        verifiedBreakpoint.source.path
                    ),
                    verifiedBreakpoint.line,
                    1
                ));
            }

            // 添加主程序堆栈帧
            stackFrames.push(new StackFrame(
                stackFrames.length,
                "Program.Main",
                new Source(
                    sourceName,
                    sourcePath
                ),
                1,
                1
            ));

            this.logger.debug(`创建的堆栈帧: ${JSON.stringify(stackFrames.map(frame => ({
                id: frame.id,
                name: frame.name,
                source: frame.source?.path,
                line: frame.line,
                column: frame.column
            })))}`);

            response.body = {
                stackFrames: stackFrames,
                totalFrames: stackFrames.length
            };

            this.logger.debug(`完整的响应体: ${JSON.stringify(response.body)}`);
            this.logger.info('堆栈跟踪请求处理完成');

        } catch (error) {
            this.logger.error(`获取堆栈跟踪失败: ${error}`);
            this.logger.error(`错误堆栈: ${(error as Error).stack}`);
            response.body = {
                stackFrames: [],
                totalFrames: 0
            };
        }
        this.sendResponse(response);
    }

    protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
        response.body = {
            scopes: [
                new Scope("Locals", this._variableHandles.create("locals"), false),
                new Scope("Globals", this._variableHandles.create("globals"), true)
            ]
        };
        this.sendResponse(response);
    }

    protected async disconnectRequest(response: DebugProtocol.DisconnectResponse, args: DebugProtocol.DisconnectArguments): Promise<void> {
        if (this._runtime) {
            this._runtime.kill();
            this._runtime = undefined;
        }
        this.sendResponse(response);
    }

    protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void {
        try {
            const source = args.source;
            const breakpoints = args.breakpoints || [];

            this.logger.info(`设置断点请求: ${source.path}`);
            this.logger.debug(`断点数量: ${breakpoints.length}`);

            if (!source.path) {
                throw new Error('源文件路径不能为空');
            }

            // 确保使用规范化的路径
            const normalizedSourcePath = path.normalize(source.path);
            this.logger.debug(`规范化的源文件路径: ${normalizedSourcePath}`);

            // 清除此源文件中的所有现有断点
            this._breakpointManager.removeBreakpoint(normalizedSourcePath, -1);

            // 设置新的断点
            const breakpointsToVerify: Breakpoint[] = [];
            for (const bp of breakpoints) {
                try {
                    // 创建断点对象
                    const breakpoint = this._breakpointManager.addBreakpoint({
                        source: new Source(
                            path.basename(normalizedSourcePath),
                            normalizedSourcePath,
                            undefined,
                            undefined,
                            'normal'
                        ),
                        line: bp.line || 0,
                        column: bp.column,
                        condition: bp.condition,
                        hitCondition: bp.hitCondition,
                        logMessage: bp.logMessage,
                        enabled: true,
                        verified: true  // 先设置为已验证
                    });

                    this.logger.debug(`添加断点: ${JSON.stringify({
                        id: breakpoint.id,
                        path: normalizedSourcePath,
                        line: breakpoint.line,
                        verified: breakpoint.verified
                    })}`);

                    // 创建要返回的断点对象
                    const verifiedBreakpoint = new Breakpoint(
                        true,  // 设置为已验证
                        breakpoint.line,
                        breakpoint.column,
                        new Source(
                            path.basename(normalizedSourcePath),
                            normalizedSourcePath
                        )
                    );

                    breakpointsToVerify.push(verifiedBreakpoint);

                    // 发送断点变更事件
                    this.sendEvent(new BreakpointEvent('new', verifiedBreakpoint));

                } catch (error: unknown) {
                    this.logger.error(`设置断点失败: ${error instanceof Error ? error.message : String(error)}`);
                }
            }

            response.body = {
                breakpoints: breakpointsToVerify
            };

            this.logger.debug(`返回的断点信息: ${JSON.stringify(response.body)}`);
            this.sendResponse(response);

        } catch (error: unknown) {
            this.logger.error(`设置断点请求失败: ${error instanceof Error ? error.message : String(error)}`);
            response.success = false;
            response.message = String(error);
            this.sendResponse(response);
        }
    }

    private async verifyBreakpointLocation(breakpoint: IBreakpoint): Promise<void> {
        try {
            // TODO: 实现与调试器的通信来验证断点位置
            // 这里应该发送命令到调试器来验证断点是否可以设置
            this.logger.debug(`验证断点位置: ${breakpoint.source.path}:${breakpoint.line}`);
        } catch (error) {
            this.logger.error(`验证断点位置失败: ${error}`);
        }
    }

    private resolveProgramPath(programPath: string): string {
        if (path.isAbsolute(programPath)) {
            return programPath;
        }
        return path.resolve(programPath);
    }

    private async sendDebuggerCommand(command: string): Promise<void> {
        try {
            if (!this._runtime) {
                throw new Error('调试器未启动');
            }

            if (!this._runtime.stdin) {
                throw new Error('调试器标准输入不可用');
            }

            // 添加调试器命令前缀
            const fullCommand = `-exec ${command}`;
            this._runtime.stdin.write(fullCommand + '\n');
            this.logger.debug(`发送调试器命令: ${fullCommand}`);

            // 等待命令执行完成
            await new Promise(resolve => setTimeout(resolve, 100));
        } catch (error) {
            this.logger.error(`发送调试器命令失败: ${error}`);
            throw error;
        }
    }
} 